package com.lq.learningTest.junit;

import org.junit.Test;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;

public class PerformanceTest {

    @Test
    /**比较hashtableTest与ConcurrentHashMap的性能差距
     *
     */
    public void hashtableTest() throws InterruptedException {
        final Map<Integer,Integer> map = new Hashtable<>(500000000);
        final CountDownLatch countDownLatch = new CountDownLatch(50);
        System.out.println("----------------开始测试Hashtable------------------");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 50; i++) {
            final int j = i;
            new Thread(()->{
                for (int k = 0; k < 100000; k++) {
                    map.put(j*k,1);
                }
                countDownLatch.countDown();
            }).start();
        }
        countDownLatch.await();
        long end = System.currentTimeMillis();
        System.out.println("hashtable:(end-start) = " + (end - start));

        // ----------------开始测试ConcurrentHashMap------------------
        System.out.println("----------------开始测试ConcurrentHashMap------------------");
        final Map map2 = new ConcurrentHashMap<>(500000000);
        final CountDownLatch countDownLatch2 = new CountDownLatch(50);
        start = System.currentTimeMillis();
        for (int i = 0; i < 50; i++) {
            final int j = i;
            new Thread(()->{
                for (int k = 0; k < 100000; k++) {
                    map2.put(j*k,1);
                }
                countDownLatch2.countDown();
            }).start();
        }
        countDownLatch.await();
        end = System.currentTimeMillis();
        System.out.println("ConcurrentHashMap:(end-start) = " + (end - start));
    }

    @Test
    /**
     * 尾插：
     * ArrayList:220ms（顺序插入，直接往里面放数据，满了又继续扩容）
     * LinkedList:851ms（每个数据有前后引用的指向，所以性能要差一点）
     */
    public void testArrayListAdd(){
        List<Integer> list = new ArrayList<>();
        Long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            list.add((int)(Math.random()*100));
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }

    @Test
    public void testLinkedListAdd(){
        List<Integer> list = new LinkedList<>();
        Long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            list.add((int)(Math.random()*100));
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }



    @Test
    /**
     * 头插：
     *ArrayList:出不来结果（速度非常非常慢，因为每插入一个数据，以往的每个数据都要往后面挪一个位置出来，开销太大了）
     * LinkedList:861ms，对于链表来说头插和尾插没有多大区别
     */
    public void testArrayListAddHeader(){
        List<Integer> list = new ArrayList<>();
        Long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            list.add(0,(int)(Math.random()*100));
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }

    @Test
    public void testLinkedListAddHeader(){
        List<Integer> list = new LinkedList<>();
        Long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            list.add(0,(int)(Math.random()*100));
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }





    @Test
    /**
     * 迭代获取：
     * ArrayList：2ms(非常快，有下标，直接就能拿到,ArrayList还有性能调整，会判断i是在前半段还是后半段，前半段就
     * 从头开始往后面查找，后半段就从尾巴开始往前面查找）
     * LinkedList：出不来结果(他拿每个第i个数据，都要从头开始，通过地址引用指向去寻找）
     */
    public void testArrayListFor(){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add((int)(Math.random()*100));
        }
        System.out.println("开始------");
        Long start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }

    @Test
    public void testLinkedListFor(){
        List<Integer> list = new LinkedList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add((int)(Math.random()*100));
        }
        System.out.println("开始------");
        Long start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }


    @Test
    /**
     * 使用迭代器去迭代
     * ArrayList:3ms（很快，这里数据不明显，如果我们设置为100000000，迭代器为4ms，使用for迭代为8ms,
     * 因为迭代器在遍历过程中，每遍历一个数据，就直接往下继续遍历了，不会又回到头重新开始遍历)
     * LinkedLIst:29ms（这里就有结果了，因为只管往下遍历查询下个数据的引用地址，直到查询完毕，中途每个数据不会重头再来）
     *
     * 所以对于遍历来说，无论是使用for循环还是迭代器，都是基于数组的ArrayList快，所以在工作中89%使用到的都是ArrayList
     */
    public void testArrayListIterator(){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add((int)(Math.random()*100));
        }
        System.out.println("开始------");
        Long start = System.currentTimeMillis();
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            iterator.next();
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }

    @Test
    public void testLinkedListIterator(){
        List<Integer> list = new LinkedList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add((int)(Math.random()*100));
        }
        System.out.println("开始------");
        Long start = System.currentTimeMillis();
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            iterator.next();
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);

    }




    @Test
    /**
     * 随机删除
     * ArrayList:出不来结果（基于数组的ArrayList在执行删除操作的过程中，存在移位的过程，就是后续的内容往删除的位置上挪动，开销大）
     * 但是对于尾巴上按顺序往前做删除操作就非常快了，删除对应内容，size减一就完了
     * LinkedList:30ms（对于链表来说就是改变引用指向的问题）
     *
     * 所以在中间做操作一定是LinkedList快
     * 在尾巴上做操作，一定是ArrayList快（工作场景中，80%都是尾巴上做操作）
     */
    public void testLinkedListDel(){
        List<Integer> list = new LinkedList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add(0,(int)(Math.random()*100));
        }
        Long start = System.currentTimeMillis();
        // 不用管为啥，这就是排序，复制过来用就行，写个冒泡也行
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            if(iterator.next()>5000000){
                iterator.remove();
            }
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }


    @Test
    public void testArrayListDel(){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            list.add(0,(int)(Math.random()*100));
        }
        Long start = System.currentTimeMillis();
        // 不用管为啥，这就是排序，复制过来用就行，写个冒泡也行
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            if(iterator.next()>5000000){
                iterator.remove();
            }
        }
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }

    //迭代过程，能用迭代器就是迭代器，性能会更快
    //仅仅是迭代，过程中不删除数据，我们就用ArrayList
    //必须使用头插，就用LinkedList

    @Test
    public void testArrayListSort(){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            list.add(0,(int)(Math.random()*100));
        }
        Long start = System.currentTimeMillis();
        // 不用管为啥，这就是排序，复制过来用就行，写个冒泡也行
        list.sort(Comparator.comparingInt(num -> num));
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }


    @Test
    public void testLinkedListSort(){
        List<Integer> list = new LinkedList<>();
        for (int i = 0; i < 100000; i++) {
            list.add(0,(int)(Math.random()*100));
        }
        Long start = System.currentTimeMillis();
        // 不用管为啥，这就是排序，复制过来用就行，写个冒泡也行
        list.sort(Comparator.comparingInt(num -> num));
        Long end = System.currentTimeMillis();
        System.out.printf("用时%d毫秒。",end-start);
    }


}

